package com.linking.web.common;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.borealis.common.utils.lock.AbstractIdLock;
import com.google.common.collect.Lists;
import com.linking.config.manager.MainConfigManager;
import com.linking.config.pojo.BootstrapConfigBO;
import com.linking.redis.manager.RedisLock;
import com.linking.web.manager.ResultManager;
import com.linking.web.manager.redis.RedisSelfUtils;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * Bean获取类
 *
 * @author yaoweixin
 * @date 2019/01/21
 */
@Slf4j
public abstract class AbstractBeans implements ApplicationContextAware {

  @Resource
  RedisSelfUtils redisUtils;

  private static int workerId = -1;

  private static int dataId = -1;

  private static String idWorkerName = "";

  private static String idWorkerKey = "";

  private static ApplicationContext ctx;

  @Override
  public void setApplicationContext(@Nonnull ApplicationContext ac) throws BeansException {
    ctx = ac;
    initByStart();
  }

  /**
   * 启动时出示
   */
  public abstract void initByStart();

  public static Object getBean(String name) {
    return ctx.getBean(name);
  }

  public static <T> T getBean(Class<T> clz) {
    return ctx.getBean(clz);
  }

  public static ResultManager getResult() {
    return ctx.getBean(ResultManager.class);
  }

  public static ApplicationContext getCtx() {
    return ctx;
  }

  private static AbstractIdLock idLock = null;

  public static AbstractIdLock getIdLock() {
    if (idLock == null) {
      idLock = new RedisLock("LOCK:Linking:Web:", ctx.getBean(RedisSelfUtils.class));
    }
    return idLock;
  }

  public static void initIdWorker(String name, String idWorkerInfo) {
    idWorkerName = name;
    idWorkerKey = idWorkerInfo;
    RedisSelfUtils redisSelfUtils = AbstractBeans.getBean(RedisSelfUtils.class);
    String key = "IdWorker:key:" + name + ":" + idWorkerInfo;
    String value = (String) redisSelfUtils.get(key);
    if (StrUtil.isEmpty(value)) {
      createIdWorker();
    } else {
      String[] temp = value.split(",");
      workerId = Integer.parseInt(temp[0]);
      dataId = Integer.parseInt(temp[1]);
      if (log.isDebugEnabled()) {
        log.debug("initIdWorker=======workerId={}, dataId={}", workerId, dataId);
      }
    }
    if (log.isDebugEnabled()) {
      log.debug("initIdWorker=======idWorkerKey={}", idWorkerKey);
    }
  }

  public static void refreshIdWorker() {
    if (workerId == -1) {
      return;
    }
    RedisSelfUtils redisSelfUtils = AbstractBeans.getBean(RedisSelfUtils.class);
    String key = "IdWorker:id:" + idWorkerName + ":" + workerId + "-" + dataId;
    String key2 = "IdWorker:key:" + idWorkerName + ":" + idWorkerKey;
    redisSelfUtils.expire(key, 3L * 60);
    redisSelfUtils.expire(key2, 3L * 60);
  }

  private static void createIdWorker() {
    RedisSelfUtils redisSelfUtils = AbstractBeans.getBean(RedisSelfUtils.class);
    int sizeX = 32;
    int sizeY = 32;
    // 初始二维数组，代表机房编号与机器编号
    List<List<Integer>> list = Lists.newArrayList();
    List<Integer> indexList = Lists.newArrayList();
    for (int i = 0; i < sizeX; i++) {
      indexList.add(i);
      List<Integer> yList = Lists.newArrayList();
      for (int j = 0; j < sizeY; j++) {
        yList.add(j);
      }
      list.add(yList);
    }
    // 随机一个值，并判断是否可用
    int size = sizeX * sizeY;
    for (int i = 0; i < size; i++) {
      int xIndex = RandomUtil.randomInt(0, indexList.size());
      int x = indexList.get(xIndex);
      int yIndex = RandomUtil.randomInt(0, list.get(x).size());
      int y = list.get(x).get(yIndex);
      list.get(x).remove(yIndex);
      if (list.get(x).isEmpty()) {
        indexList.remove(xIndex);
      }
      String key = "IdWorker:id:" + idWorkerName + ":" + x + "-" + y;
      if (redisSelfUtils.setIfAbsent(key, idWorkerKey, 3L, TimeUnit.MINUTES)) {
        String key2 = "IdWorker:key:" + idWorkerName + ":" + idWorkerKey;
        redisSelfUtils.set(key2, x + "," + y, 3L, TimeUnit.MINUTES);
        workerId = x;
        dataId = y;
        if (log.isDebugEnabled()) {
          log.debug("createIdWorker=======workerId={}, dataId={}", workerId, dataId);
        }
        break;
      }
      try {
        Thread.sleep(1);
      } catch (InterruptedException ignored) {
      }
    }
  }

  public static Snowflake genIdWorker(int data) {
    int id = workerId;
    if (id == -1) {
      BootstrapConfigBO configBo = MainConfigManager.getBootstrapConfig();
      id = configBo.getWorkerId();
    }
    return IdUtils.genIdWorker(id, dataId > -1 ? dataId : data);
  }
}